#include "core/main.h"

void GameModelFrame::RecordSpriteWindow(const GameModelParameters &parameters, int x, int y, short *features, int &featureIndex) const
{
    features[featureIndex++] = x;
    features[featureIndex++] = y;

    unordered_map<UINT64, int> coordToFeatureIndex;

    for(int xDelta = -parameters.stateWindowSize; xDelta <= parameters.stateWindowSize; xDelta++)
    {
        for(int yDelta = -parameters.stateWindowSize; yDelta <= parameters.stateWindowSize; yDelta++)
        {
            coordToFeatureIndex[Utility::MakeInt64(x + xDelta, y + yDelta)] = featureIndex;
            features[featureIndex++] = -1;
        }
    }

    for(UINT spriteIndex = 0; spriteIndex < sourceFrame->sprites.size(); spriteIndex++)
    {
        const SpriteLocation &curSprite = sourceFrame->sprites[spriteIndex];
        auto it = coordToFeatureIndex.find(Utility::MakeInt64(curSprite.coord.x / parameters.gridScale, curSprite.coord.y / parameters.gridScale));
        if(it != coordToFeatureIndex.end())
        {
            features[it->second] = curSprite.spriteID;
        }
    }
}

void GameModelFrame::ComputeSpriteCounts(const GameModelParameters &parameters)
{
    spriteCount.resize(parameters.totalSpriteCount, 0);
    for(auto spriteIterator = sourceFrame->sprites.begin(); spriteIterator != sourceFrame->sprites.end(); spriteIterator++)
    {
        UINT curSpriteID = spriteIterator->spriteID;
        if(curSpriteID < parameters.totalSpriteCount)
        {
            spriteCount[curSpriteID]++;
        }
    }
}

void GameModelFrame::MakeFeatureVector(const GameModelParameters &parameters, int x, int y, short *features)
{
    int featureIndex = 0;
    for(int frameHistoryIndex = 0; frameHistoryIndex < parameters.historyFrameCount; frameHistoryIndex++)
    {
        const GameModelFrame &historyFrame = *historyFrames[frameHistoryIndex];
        
        //
        // controller
        //
        features[featureIndex++] = Utility::BoolToInt(historyFrame.sourceFrame->controller.A());
        features[featureIndex++] = Utility::BoolToInt(historyFrame.sourceFrame->controller.B());
        features[featureIndex++] = Utility::BoolToInt(historyFrame.sourceFrame->controller.left());
        features[featureIndex++] = Utility::BoolToInt(historyFrame.sourceFrame->controller.right());
        features[featureIndex++] = Utility::BoolToInt(historyFrame.sourceFrame->controller.up());
        features[featureIndex++] = Utility::BoolToInt(historyFrame.sourceFrame->controller.down());

        //
        // observed integers
        //
        for(UINT observedVariableIndex = 0; observedVariableIndex < parameters.observedIntegerCount; observedVariableIndex++)
        {
            features[featureIndex++] = historyFrame.sourceFrame->observedInts[observedVariableIndex];
        }

        //
        // sprite counts
        //
        for(UINT spriteIndex = 0; spriteIndex < historyFrame.spriteCount.size(); spriteIndex++)
        {
            features[featureIndex++] = historyFrame.spriteCount[spriteIndex];
        }

        //
        // window centered on X and Y (irrelevant for modeling variable change)
        //
        historyFrame.RecordSpriteWindow(parameters, x, y, features, featureIndex);

        //
        // feature to add: bounding-box X & Y signed distance between XY and nearest instance of each other sprite
        // deal with complexity of sprite centroids vs. top-left, and remember that XY doesn't necessarially contain a sprite.
        //

        //
        // window centered on each unique sprite
        //
        for(UINT uniqueSpriteIndex = 0; uniqueSpriteIndex < parameters.uniqueSprites.size(); uniqueSpriteIndex++)
        {
            const SpriteLocation* curSprite = historyFrame.FindUniqueSprite(parameters.uniqueSprites[uniqueSpriteIndex]);
            if(curSprite == NULL)
            {
                historyFrame.RecordSpriteWindow(parameters, -7777, -7777, features, featureIndex);
            }
            else
            {
                historyFrame.RecordSpriteWindow(parameters, curSprite->coord.x / parameters.gridScale, curSprite->coord.y / parameters.gridScale, features, featureIndex);
            }
        }
    }
}

void GameModelFrame::OverwriteDynamicFeatures(const GameModelParameters &parameters, int x, int y, short *features, bool copyFromCache)
{
    if(copyFromCache) memcpy(features, cachedFeatures, sizeof(short) * parameters.totalVariableCount);

    int featureIndex = 0;
    for(int frameHistoryIndex = 0; frameHistoryIndex < parameters.historyFrameCount; frameHistoryIndex++)
    {
        const GameModelFrame &historyFrame = *historyFrames[frameHistoryIndex];
        
        //
        // skip controller
        //
        featureIndex += 6;
        
        //
        // skip observed integers
        //
        featureIndex += parameters.observedIntegerCount;
        
        //
        // skip sprite counts
        //
        featureIndex += historyFrame.spriteCount.size();

        //
        // window centered on X and Y
        //
        historyFrame.RecordSpriteWindow(parameters, x, y, features, featureIndex);

        //
        // skip unique sprite windows
        //
        featureIndex += parameters.uniqueSprites.size() * (2 + Math::Square(parameters.stateWindowSize * 2 + 1));
    }
}
